/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: xmessage.h
 *
 * Purpose: message Base API define and implementation
 *
 * Developer:
 *   wen.gu , 2023-07-26
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __XBUS_CORE_XMESSAGE_H__
#define __XBUS_CORE_XMESSAGE_H__

#include <cstdint>
#include <memory>
#include <functional>
#include <assert.h>

#include "xbus/core/xtype.h"
#include "xbus/core/xbuffer.h"


#define XMSG_START_CODE 0xabcd
#define XMSG_START_CODE_BYTE1 0xcd
#define XMSG_START_CODE_BYTE2 0xab

#define XMSG_PROTOCOL_VER (1)
/**unkown message type */
#define XMSG_UNKNOWN_TYPE (0)
#define XMSG_GET_PROTOCOL_VER(flags) ((flags) & 0x0f)


#define XMSG_FLAGS_TP_FLAG_MASK (0x80)
#define XMSG_FLAGS_MASK (0xf0)

#define XMSG_FLAGS_TP_FLAG(flags) ((flags) & XMSG_FLAGS_TP_FLAG_MASK)
#define XMSG_FLAGS(flags) ((flags) & XMSG_FLAGS_MASK)

#define XMSG_FLAGS_TP_FLAG_ENABLE(flags)  do{flags |= XMSG_FLAGS_TP_FLAG_MASK;}while(0)
#define XMSG_FLAGS_TP_FLAG_DISABLE(flags) do{flags &= (~XMSG_FLAGS_TP_FLAG_MASK);}while(0)

#define XMSG_HDR_LENGTH(flags) (XMSG_FLAGS_TP_FLAG(flags) ? (sizeof(core::XMessageHeader) + sizeof(core::XMessageHeaderExtension)): sizeof(core::XMessageHeader))

/**for base header */
#define XMSG_FIELD_OFFSET_LENGTH (0)
#define XMSG_FIELD_OFFSET_STARTCODE (4)
#define XMSG_FIELD_OFFSET_PROTOCOL_VER (6)
#define XMSG_FIELD_OFFSET_TYPE (7)

/** for extension header about core protocol(unpacking/pacakging) */
#define XMSG_FIELD_OFFSET_TOTAL_PKG (8)
#define XMSG_FIELD_OFFSET_PKG_IDX   (12)

#define XMSG_PKG_IDX_START (1)
#define XMSG_PKG_IDX_INVALID (0)


namespace xbus::core {

struct XMessageHeader {
    uint32_t length;  /** total message size = header size + payloaf size */
    uint16_t start_code;
    /**
     * bit 4~7:   flags,
     *      - bit 7:  tp flag: 
     *                  - 1: indicat a big packege will sent to target by split to many segments(e.g core with udp). 
     *                  - 0: just a normal message
     *      - bit 4~6: reserved
     * bit 0~3: the protocol version. range is: 1 ~ 15, now is 1
    */
    uint8_t protocol_version;
    uint8_t type;
};

class XMessageHeader {
public:
    XMessageHeader(XBufferPtr buf_ptr);
    virtual ~XMessageHeader() = default;

public:
    uint32_t length();
    uint16_t start_code();
    uint8_t start_code(uint32_t byte_idx);
    uint8_t protocol_version();
    uint8_t flags();
    uint8_t  type();
    uint32_t hdr_size();
};


struct XMessageHeaderExtension {
    uint32_t total_pkg; /** a big message split to many pacakge */
    uint32_t pkg_idx;   /** the index of current package in a big message */
};

class XMessage {
public:
    using XComId = xbus::core::XComId;
    using XBufferPtr = core::XBufferPtr;
    using XMessagePtr = std::shared_ptr<XMessage>;

protected:
    XMessage(XBufferPtr buffer); 
    XMessage(uint32_t capacity); 
    bool initialize(uint8_t type, uint8_t flags, uint32_t total_pkg = 0);

public:
    
    virtual ~XMessage();
public: /** for base Header */
    virtual uint8_t* data();
    virtual uint32_t length();
    virtual uint16_t start_code();
    virtual uint8_t protocol_version();

    virtual uint8_t type();
    virtual void set_type(uint8_t type);

    virtual uint8_t flags();

    virtual uint8_t* payload();
    virtual uint32_t payload_size();


    virtual uint32_t pkg_idx();
    virtual void set_pkg_idx(uint32_t idx);
    virtual uint32_t total_pkg(); 
    virtual void set_total_pkg(uint32_t total); 
    virtual bool is_tp_message();  /**true: is core message, a package will sent by split to many segment */

public:
    virtual XBufferPtr buffer();
    virtual bool assign(uint32_t payload_size); /** assign a payload size */
    /** just fill data to payload segment from start_pos position, 
     * if (start_pos + size) > payload size, then the message buffer will resize to bigger than start_pos + size 
      */
    virtual bool fill_payload(const uint8_t* data, uint32_t size, uint32_t start_pos = 0); 

    virtual bool append_payload(const uint8_t* data, uint32_t size); /** will be add the length of payload and total message */

public:
    /**total_pkg be used when create Tp Message only*/
    static XMessagePtr Create(uint8_t type, uint8_t flags, uint32_t capacity, uint32_t total_pkg = 0);
    static XMessagePtr CreateMsg(uint8_t type, uint32_t capacity);
    static XMessagePtr CreateMsg(XBufferPtr buffer);
    /** directly copy a complete message buffer to intialize XMessage*/
    static XMessagePtr CreateMsg(uint8_t* msg_buf , uint32_t length); 
    static XMessagePtr CreateTpMsg(uint8_t type, uint32_t capacity, uint32_t total_pkg);


    static bool CheckMessageHeader(const XMessageHeader& hdr); /** check message header is right */  
    static bool CheckMessageHeader(const uint8_t* buf , uint32_t size);  

public: /** extension API, just as helper API, not as the data which to be sent */
    /** Indicate where the message com from, just used for receiver */
    virtual XComId src_com_id() const { return src_com_id_; }
    virtual void set_src_com_id(XComId fd) { src_com_id_ = fd; } //this will be set by 'server' API

protected:
    XComId src_com_id_ = INVALID_XCOM_ID;
    XBufferPtr buffer_ = nullptr;
};


using XMessagePtr = XMessage::XMessagePtr;


} //namespace xbus::core


#endif /** !__XBUS_CORE_XMESSAGE_H__ */